Add-Type @"
using System;
using System.Runtime.InteropServices;
public class WinAPI {
    public const int SW_RESTORE = 9;
    public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
    public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
    public const uint SWP_NOMOVE = 0x0002;
    public const uint SWP_NOSIZE = 0x0001;
    public const uint SWP_SHOWWINDOW = 0x0040;

    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    public static extern bool IsIconic(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

    [DllImport("user32.dll")]
    public static extern bool FlashWindow(IntPtr hwnd, bool bInvert);
}
"@

function Bring-ProcessToFront {
    param([System.Diagnostics.Process]$proc)
    $hwnd = $proc.MainWindowHandle
    if ($hwnd -eq 0) { return $false }

    # 如果最小化，先还原
    if ([WinAPI]::IsIconic($hwnd)) {
        [WinAPI]::ShowWindowAsync($hwnd, [WinAPI]::SW_RESTORE) | Out-Null
    }

    # 先置顶
    [WinAPI]::SetWindowPos($hwnd, [WinAPI]::HWND_TOPMOST, 0, 0, 0, 0, `
        [WinAPI]::SWP_NOMOVE -bor [WinAPI]::SWP_NOSIZE -bor [WinAPI]::SWP_SHOWWINDOW) | Out-Null

    # 再取消置顶，避免长时间顶层
    Start-Sleep -Milliseconds 100
    [WinAPI]::SetWindowPos($hwnd, [WinAPI]::HWND_NOTOPMOST, 0, 0, 0, 0, `
        [WinAPI]::SWP_NOMOVE -bor [WinAPI]::SWP_NOSIZE -bor [WinAPI]::SWP_SHOWWINDOW) | Out-Null

    # 设置前台窗口
    [WinAPI]::SetForegroundWindow($hwnd) | Out-Null

    # 闪烁任务栏图标（可选）
    [WinAPI]::FlashWindow($hwnd, $true) | Out-Null

    return $true
}


function Parse-QueryString {
    param([string]$queryString)
    $dict = @{}
    if ($queryString.StartsWith("?")) {
        $queryString = $queryString.Substring(1)
    }
    $pairs = $queryString -split "&"
    foreach ($pair in $pairs) {
        if ($pair -match "=") {
            $parts = $pair -split "=", 2
            $key = [System.Uri]::UnescapeDataString($parts[0])
            $value = [System.Uri]::UnescapeDataString($parts[1])
            $dict[$key] = $value
        }
    }
    return $dict
}

$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add("http://127.0.0.1:33228/")
$listener.Start()
Write-Host "Listening on http://127.0.0.1:33228/ ..."

while ($true) {
    $context = $listener.GetContext()
    $request = $context.Request
    $response = $context.Response

    if ($request.HttpMethod -eq "GET" -and $request.Url.AbsolutePath -eq "/runExe") {
        $query = Parse-QueryString $request.Url.Query
        $exePath = $query["index"]
        if ([string]::IsNullOrEmpty($exePath)) {
            $response.StatusCode = 400
            $buffer = [System.Text.Encoding]::UTF8.GetBytes("Missing index parameter.")
            $response.OutputStream.Write($buffer, 0, $buffer.Length)
            $response.Close()
            continue
        }
        # 去掉可能的引号和空格
        $exePath = $exePath.Trim(' "', "'")
        if (-Not (Test-Path $exePath)) {
            $response.StatusCode = 404
            $buffer = [System.Text.Encoding]::UTF8.GetBytes("Exe file not found.")
            $response.OutputStream.Write($buffer, 0, $buffer.Length)
            $response.Close()
            continue
        }

        # 取exe文件名，不带路径，作为进程名的参考
        $exeFileName = [System.IO.Path]::GetFileNameWithoutExtension($exePath)

        # 查找是否已有进程运行该exe
        $procs = Get-Process | Where-Object {
            $_.Path -and ($_.Path -eq (Resolve-Path $exePath).Path)
        }

        if ($procs.Count -gt 0) {
            # 已经运行，取第一个
            $proc = $procs[0]
            $success = Bring-ProcessToFront -proc $proc
            $msg = if ($success) { "Brought existing process to front." } else { "Process running but no window to bring front." }
        } else {
            # 启动程序
            try {
                $startInfo = New-Object System.Diagnostics.ProcessStartInfo
                $startInfo.FileName = $exePath
                $proc = [System.Diagnostics.Process]::Start($startInfo)

                # 等待窗口句柄初始化，最多等5秒
                $waitCount = 0
                while (($proc.MainWindowHandle -eq 0) -and ($waitCount -lt 50)) {
                    Start-Sleep -Milliseconds 100
                    $proc.Refresh()
                    $waitCount++
                }
                $success = Bring-ProcessToFront -proc $proc
                $msg = if ($success) { "Started process and brought to front." } else { "Started process but no window to bring front." }
            }
            catch {
                $response.StatusCode = 500
                $buffer = [System.Text.Encoding]::UTF8.GetBytes("Failed to start process: $_")
                $response.OutputStream.Write($buffer, 0, $buffer.Length)
                $response.Close()
                continue
            }
        }

        $response.StatusCode = 200
        $buffer = [System.Text.Encoding]::UTF8.GetBytes($msg)
        $response.OutputStream.Write($buffer, 0, $buffer.Length)
        $response.Close()
    }
    else {
        $response.StatusCode = 404
        $buffer = [System.Text.Encoding]::UTF8.GetBytes("Not Found")
        $response.OutputStream.Write($buffer, 0, $buffer.Length)
        $response.Close()
    }
}
